using System;
using System.Collections.Generic;
using System.Management.Automation;
using System.IO;

namespace PSSymbolStore.Commands
{
    [Cmdlet(VerbsCommon.Remove, "Transaction", SupportsShouldProcess = true)]
    public class RemoveTransactionCommand : TransactionCommandBase
    {
        protected override bool CreateSymbolStore
        {
            get
            {
                return false;
            }
        }

        protected override void ProcessTransaction(string transactionId)
        {
            DeleteTransaction(transactionId);

            base.ProcessTransaction(transactionId);
        }

        private void DeleteTransaction(string id)
        {
            // 0. Check for the "transactionid.locked" file indicating the transaction has been marked as locked
            if (File.Exists(SymbolStoreObject.GetTransactionLockedFile(id)))
            {
                // The transaction is locked, indicate an error
                WriteError(new ErrorRecord(new SymbolStoreException("Transaction " + id + " is locked, unable to delete"), "Unable to delete transaction id " + id, ErrorCategory.InvalidOperation, id));
                return;
            }

            string transactionFile = SymbolStoreObject.GetTransactionFile(id);

            // 1. Create and commit a new "deletion" transaction entry
            var deleteTrans = new DeleteTransaction
                                  {
                                      TransactionId = SymbolStoreObject.GetNextTransactionId(),
                                      DeletedTransactionId = id
                                  };
            bool shouldProcess = SymbolStoreObject.CommitTransaction(deleteTrans);

            if (shouldProcess)
            {
                // 2. Check for a "transactionid" file
                if (!File.Exists(transactionFile))
                {
                    WriteError(new ErrorRecord(new SymbolStoreException("Transaction " + id + " not found"), "Transaction " + id + " not found", ErrorCategory.InvalidData, id));
                    return;
                }

                // 3. Delete the transaction:
                //    3.1 Read in the transaction file
                using (StreamReader reader = File.OpenText(transactionFile))
                {
                    string line = reader.ReadLine();
                    while (line != null)
                    {
                        ProcessTransactionLine(id, line);
                        line = reader.ReadLine();
                    }
                }

                //    3.4 Move the transaction id file to "transactionid.deleted"
                File.Move(transactionFile, String.Format("{0}.deleted", transactionFile));

                //    3.5 Delete the entry from the server.txt file, if appropriate
                RemoveTransactionFromServerFile(id);

                WriteVerbose("Transaction " + id + " deleted");
            }
        }

        private void ProcessTransactionLine(string transactionId, string line)
        {
            if (String.IsNullOrEmpty(line))
            {
                // Ignore if it's blank
                return;
            }

            List<string> parts = Helpers.ParseCSVLine(line);
            if ((parts != null) && (parts.Count == 2))
            {
                string subDir = parts[0].Trim('"');
                string originalFilePath = parts[1].Trim('"');
                string originalFileName = Path.GetFileName(originalFilePath);
                if (!String.IsNullOrEmpty(subDir))
                {
                    //    3.2 Remove transaction entry from the refs.ptr file in the appropriate subdirectory
                    bool moreUncompressedFileRefs, moreCompressedFileRefs, morePointerRefs;
                    RefsFileEntry lastEntry;
                    string refFile = Path.Combine(SymbolStoreObject.Location, Path.Combine(subDir, PSSymbolStore.SymbolStore.REFS_PTR));
                    string[] newRefsPtrFileContents = RemoveTransactionFromRefsFile(transactionId, refFile, out moreUncompressedFileRefs, out moreCompressedFileRefs, out morePointerRefs, out lastEntry);
                    //    3.3 If refs.ptr is empty, delete the entire file/subdir
                    if ((newRefsPtrFileContents == null) || (newRefsPtrFileContents.Length == 0))
                    {
                        // Issue #5759
                        // The directory may not exist if the object is in the transaction twice.
                        // So, check before deleting it.
                        string refDir = Path.Combine(SymbolStoreObject.Location, subDir);
                        if (Directory.Exists(refDir))
                        {
                            Directory.Delete(refDir, true);
                        }
                    }
                    else
                    {
                        // Update the new contents of refs.ptr
                        File.WriteAllLines(refFile, newRefsPtrFileContents);

                        string filePath = Path.Combine(Path.Combine(SymbolStoreObject.Location, subDir), originalFileName);

                        if (!moreUncompressedFileRefs)
                        {
                            if (File.Exists(filePath))
                            {
                                // If there aren't any remaining uncompressed "file" transactions
                                // then delete the physical file.
                                File.Delete(filePath);
                            }
                        }

                        if (!moreCompressedFileRefs)
                        {
                            string compressedFilePath = Helpers.GetCompressedFilePath(filePath);
                            if (File.Exists(compressedFilePath))
                            {
                                // If there aren't any remaining compressed "file" transactions
                                // then delete the physical file.
                                File.Delete(compressedFilePath);
                            }
                        }

                        string filePtrPath = Path.Combine(Path.Combine(SymbolStoreObject.Location, subDir), PSSymbolStore.SymbolStore.FILE_PTR);
                        bool filePtrExists = File.Exists(filePtrPath);
                        if (!morePointerRefs && filePtrExists)
                        {
                            File.Delete(filePtrPath);
                        }
                        else if ((lastEntry != null) && (lastEntry.TransactionUploadType == TransactionUploadType.Pointer)
                            && !filePtrExists && !String.IsNullOrEmpty(lastEntry.FilePath.Trim('"')))
                        {
                            // The last entry in the refs.ptr file is a pointer, create the file.ptr file
                            File.WriteAllText(filePtrPath, lastEntry.FilePath.Trim('"'));
                        }
                    }
                }
            }
        }

        private static string[] RemoveTransactionFromRefsFile(string id, string refFile, out bool moreUncompressedFileRefs, out bool moreCompressedFileRefs, out bool morePointerRefs, out RefsFileEntry lastEntry)
        {
            List<string> newContents = new List<string>();
            moreUncompressedFileRefs = false;
            moreCompressedFileRefs = false;
            morePointerRefs = false;
            lastEntry = null;

            if (File.Exists(refFile))
            {
                using (StreamReader reader = File.OpenText(refFile))
                {
                    string transaction = reader.ReadLine();
                    while (transaction != null)
                    {
                        if (!String.IsNullOrEmpty(transaction) && !transaction.StartsWith(id, StringComparison.InvariantCultureIgnoreCase))
                        {
                            newContents.Add(transaction);

                            // Check if it's a file or ptr
                            List<string> transactionData = Helpers.ParseCSVLine(transaction);
                            lastEntry = new RefsFileEntry(transactionData);

                            if (!moreUncompressedFileRefs || !morePointerRefs || !moreCompressedFileRefs)
                            {
                                // Check if it's a file or ptr
                                if (lastEntry.TransactionUploadType == TransactionUploadType.File)
                                {
                                    if (lastEntry.IsCompressed)
                                    {
                                        moreCompressedFileRefs = true;
                                    }
                                    else
                                    {
                                        moreUncompressedFileRefs = true;
                                    }
                                }
                                else
                                {
                                    morePointerRefs = true;
                                }
                            }
                        }

                        transaction = reader.ReadLine();
                    }
                }
            }

            return newContents.ToArray();
        }

        private void RemoveTransactionFromServerFile(string id)
        {
            bool found = false;
            List<string> newContents = new List<string>();

            using (StreamReader reader = File.OpenText(SymbolStoreObject.ServerFile))
            {
                string line = reader.ReadLine();
                while (line != null)
                {
                    if (!String.IsNullOrEmpty(line))
                    {
                        if (!line.StartsWith(id, StringComparison.InvariantCultureIgnoreCase))
                        {
                            newContents.Add(line);
                        }
                        else
                        {
                            found = true;
                        }
                    }

                    line = reader.ReadLine();
                }
            }

            // Write out the server.txt file if the transaction was removed
            if (found)
            {
                File.WriteAllLines(SymbolStoreObject.ServerFile, newContents.ToArray());
            }
        }
    }
}
